/****************************************************************************
 * Copyright 2009 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.filter.common;

import static com.gotobject.filter.FilterQ.from;
import static com.gotobject.filter.common.Aggregations.count;
import static com.gotobject.filter.common.Predicates.eq;
import static com.gotobject.filter.internal.Objects.asIterable;
import static com.gotobject.filter.internal.Objects.asType;
import com.gotobject.filter.spi.Variant;
import com.gotobject.filter.transformation.Transformation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class Transformations {
    private Transformations(){}

    public static Transformation<Variant<String>> grep(final String substring){
        return new Transformation<Variant<String>>(){
            @Override
            public Iterable<Variant<String>> apply(Object me) throws Exception {
                final Iterable<String> lines = asIterable(me);
                final HashSet<Variant<String>>  view  = new HashSet<Variant<String>>();
                int n = 1; for(String each: lines){
                    Variant<String> v = Variant.empty();
                    if(each.contains(substring)){
                        v.place(String.valueOf(n), each);
                        view.add(v);
                    }
                    n++;
                }
                return view;
            }

            @Override
            public String toString() {
                return "grep(" + substring + ")";
            }
        };
    }

    public static <R> Transformation<R> indexed(final R... target){
        return new Transformation<R>(){
            @Override
            public Iterable<R> apply(Object x) throws Exception {
                if(target.length == 0 || Arrays.asList(target).contains(null)) throw new IllegalArgumentException();
                final List<R> rList = new ArrayList<R>();
                final List<Object> tList  = Iterables.asList(asIterable(x));
                final List<R> toTransform = Iterables.asList(Arrays.asList(target));
                for(Object each : tList){
                    rList.add(toTransform.get(asType(Integer.class, each))); // we assume that indexes r interger-based indexes...
                }
                return rList;
            }

            @Override
            public String toString() {
                return "indexed(" + Arrays.asList(target) + ")";
            }
        };
    }

    public static Transformation<Variant<Integer>> occurrence(final String... words){
        return new Transformation<Variant<Integer>>(){
            @Override
            public Iterable<Variant<Integer>> apply(Object me) throws Exception {
                final ArrayList<Variant<Integer>> r = new ArrayList<Variant<Integer>>();
                final Iterable<Object>   i = asIterable(me);
                for(String w : words){
                    final Variant<Integer> p = Variant.empty();
                    int count = 0;
                    for(Object eL: i){
                        final String eachLine = (String) eL;
                        count += count(from(eachLine).where(eq(w)).select());
                        p.place(w, count);
                    }
                    r.add(p);

                }
                return r;
            }

            @Override
            public String toString() {
                return "occurrence(" + Arrays.asList(words) + ")";
            }
        };
    }

    @SuppressWarnings({"RedundantTypeArguments"})
    public static <T> Transformation<T> minus(int amount){
        return Transformations.<T>plus(-amount);
    }


    @SuppressWarnings({"RedundantTypeArguments"})
    public static <T> Transformation<T> minus(float amount){
        return Transformations.<T>plus(-amount);
    }

    public static <R> Transformation<R> plus(final float amount){
        return new Transformation<R>(){
            @Override
            public Iterable<R> apply(Object x) throws Exception {
                final Iterable<Object>  input   = asIterable(x);
                final List<R>           result  = new ArrayList<R>();

                for(Object each : input){
                    final Float total =  asType(Float.class, each) + amount;
                    //noinspection unchecked
                    result.add((R) total);
                }
                return result;
            }

            @Override
            public String toString() {
                return "plus(" + amount + ")";
            }
        };
    }

    public static <R> Transformation<R> plus(final int amount) {
        return new Transformation<R>(){
            @Override
            public Iterable<R> apply(Object x) throws Exception {
                final Iterable<Object>  input   = asIterable(x);
                final List<R>           result  = new ArrayList<R>();

                for(Object each : input){
                    final Integer total =  asType(Integer.class, each) + amount;
                    //noinspection unchecked
                    result.add((R) total);
                }
                return result;
            }

            @Override
            public String toString() {
                return "plus(" + amount + ")";
            }
        };
    }
}
